#ifdef _MSC_VER
/*
 * we do not want the warnings about the old deprecated and unsecure CRT functions
 * since these examples can be compiled under *nix as well
 */
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <winsock.h>
#define u_int unsigned int
#define u_short unsigned short
#define u_char unsigned char

#define HAVE_REMOTE

#include <pcap.h>

/* 4 bytes IP address */
typedef struct ip_address
{
    u_char byte1;
    u_char byte2;
    u_char byte3;
    u_char byte4;
} ip_address;

// 6 bytes MAC address
typedef struct mac_address
{
    u_char byte1;
    u_char byte2;
    u_char byte3;
    u_char byte4;
    u_char byte5;
    u_char byte6;
} mac_address;

/* IPv4 header */
typedef struct mac_header
{
    mac_address daddr;
    mac_address saddr;
    u_short protoclotype;
};

/* IPv4 header */
typedef struct ip_header
{
    u_char ver_ihl;         // Version (4 bits) + Internet header length (4 bits)
    u_char tos;             // Type of service
    u_short tlen;           // Total length
    u_short identification; // Identification
    u_short flags_fo;       // Flags (3 bits) + Fragment offset (13 bits)
    u_char ttl;             // Time to live
    u_char proto;           // Protocol
    u_short crc;            // Header checksum
    ip_address saddr;       // Source address
    ip_address daddr;       // Destination address
    u_int op_pad;           // Option + Padding
} ip_header;

std::fstream out;

std::map<std::string, std::string[3]> ftp;

/* prototype of the packet handler */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
std::string get_request_m_ip_message(const u_char *pkt_data);
std::string get_response_m_ip_message(const u_char *pkt_data);
void writeCSV(const struct pcap_pkthdr *header, const std::string m_ip_message);

int main()
{
    pcap_if_t *alldevs;
    pcap_if_t *d;
    int inum;
    int i = 0;
    pcap_t *adhandle;
    char errbuf[PCAP_ERRBUF_SIZE];
    u_int netmask;
    char packet_filter[] = "tcp";
    struct bpf_program fcode;

    /* Retrieve the device list */
    if (pcap_findalldevs(&alldevs, errbuf) == -1)
    {
        fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
        exit(1);
    }

    /* Print the list */
    for (d = alldevs; d; d = d->next)
    {
        printf("%d. %s", ++i, d->name);
        if (d->description)
            printf(" (%s)\n", d->description);
        else
            printf(" (No description available)\n");
    }

    if (i == 0)
    {
        printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
        return -1;
    }

    printf("Enter the interface number (1-%d):", i);
    scanf("%d", &inum);

    /* Check if the user specified a valid adapter */
    if (inum < 1 || inum > i)
    {
        printf("\nAdapter number out of range.\n");

        /* Free the device list */
        pcap_freealldevs(alldevs);
        return -1;
    }

    /* Jump to the selected adapter */
    for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++)
        ;

    /* Open the adapter */
    if ((adhandle = pcap_open_live(d->name, // name of the device
                                   65536,   // portion of the packet to capture.
                                            // 65536 grants that the whole packet will be captured on all the MACs.
                                   1,       // promiscuous mode (nonzero means promiscuous)
                                   1000,    // read timeout
                                   errbuf   // error buffer
                                   )) == NULL)
    {
        fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n");
        /* Free the device list */
        pcap_freealldevs(alldevs);
        return -1;
    }

    /* Check the link layer. We support only Ethernet for simplicity. */
    if (pcap_datalink(adhandle) != DLT_EN10MB)
    {
        fprintf(stderr, "\nThis program works only on Ethernet networks.\n");
        /* Free the device list */
        pcap_freealldevs(alldevs);
        return -1;
    }

    if (d->addresses != NULL)
        /* Retrieve the mask of the first address of the interface */
        netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
    else
        /* If the interface is without addresses we suppose to be in a C class network */
        netmask = 0xffffff;

    // compile the filter
    if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
    {
        fprintf(stderr, "\nUnable to compile the packet filter. Check the syntax.\n");
        /* Free the device list */
        pcap_freealldevs(alldevs);
        return -1;
    }

    // set the filter
    if (pcap_setfilter(adhandle, &fcode) < 0)
    {
        fprintf(stderr, "\nError setting the filter.\n");
        /* Free the device list */
        pcap_freealldevs(alldevs);
        return -1;
    }

    printf("\nlistening on %s...\n", d->description);
    out.open("FTPlog.csv", std::ios::out | std::ios::trunc);
    out << "时间,"
        << "源MAC,"
        << "源IP,"
        << "目标MAC,"
        << "目标IP,"
        << "登录名,"
        << "口令,"
        << "成功与否"
        << std::endl;
    /* At this point, we don't need any more the device list. Free it */
    pcap_freealldevs(alldevs);

    /* start the capture */
    pcap_loop(adhandle, 0, packet_handler, NULL);

    return 0;
}

/* Callback function invoked by libpcap for every incoming packet */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
    /*
     * unused parameter
     */
    (VOID)(param);

    int head = 54;
    std::string com;
    for (int i = 0; i < 4; i++)
        com += (char)pkt_data[head + i];
    if (com == "USER")
    {
        std::string m_ip_message = get_request_m_ip_message(pkt_data);
        std::string user;
        std::ostringstream sout;
        for (int i = head + 5; pkt_data[i] != 13; i++)
        {
            sout << pkt_data[i];
        }
        user = sout.str();
        ftp[m_ip_message][0] = user;
    }
    if (com == "PASS")
    {
        std::string m_ip_message = get_request_m_ip_message(pkt_data);
        std::string pass;
        std::ostringstream sout;
        for (int i = head + 5; pkt_data[i] != 13; i++)
        {
            sout << pkt_data[i];
        }
        pass = sout.str();
        ftp[m_ip_message][1] = pass;
    }
    if (com == "230 ")
    {
        std::string m_ip_message = get_response_m_ip_message(pkt_data);
        ftp[m_ip_message][2] = "SUCCEED";
        writeCSV(header, m_ip_message);
    }
    if (com == "530 ")
    {
        std::string m_ip_message = get_response_m_ip_message(pkt_data);
        ftp[m_ip_message][2] = "FAILD";
        writeCSV(header, m_ip_message);
    }
}

std::string get_request_m_ip_message(const u_char *pkt_data)
{
    mac_header *mh;
    ip_header *ih;
    std::string m_ip_message;
    std::string str; // empty string
    std::ostringstream sout;
    int length = sizeof(mac_header) + sizeof(ip_header);
    mh = (mac_header *)pkt_data;
    ih = (ip_header *)(pkt_data + sizeof(mac_header));

    sout << std::hex
         << (int)(mh->saddr.byte1) << "-"
         << (int)(mh->saddr.byte2) << "-"
         << (int)(mh->saddr.byte3) << "-"
         << (int)(mh->saddr.byte4) << "-"
         << (int)(mh->saddr.byte5) << "-"
         << (int)(mh->saddr.byte6) << ",";

    sout << std::dec
         << (int)(ih->saddr.byte1) << "."
         << (int)(ih->saddr.byte2) << "."
         << (int)(ih->saddr.byte3) << "."
         << (int)(ih->saddr.byte4) << ",";
    sout << std::hex
         << (int)(mh->daddr.byte1) << "-"
         << (int)(mh->daddr.byte2) << "-"
         << (int)(mh->daddr.byte3) << "-"
         << (int)(mh->daddr.byte4) << "-"
         << (int)(mh->daddr.byte5) << "-"
         << (int)(mh->daddr.byte6) << ",";

    sout << std::dec
         << (int)(ih->daddr.byte1) << "."
         << (int)(ih->daddr.byte2) << "."
         << (int)(ih->daddr.byte3) << "."
         << (int)(ih->daddr.byte4);
    m_ip_message = sout.str();
    return m_ip_message;
}
std::string get_response_m_ip_message(const u_char *pkt_data)
{
    mac_header *mh;
    ip_header *ih;
    std::string m_ip_message;
    std::string str; // empty string
    std::ostringstream sout;
    int length = sizeof(mac_header) + sizeof(ip_header);
    mh = (mac_header *)pkt_data;
    ih = (ip_header *)(pkt_data + sizeof(mac_header));

    sout << std::hex
         << (int)(mh->daddr.byte1) << "-"
         << (int)(mh->daddr.byte2) << "-"
         << (int)(mh->daddr.byte3) << "-"
         << (int)(mh->daddr.byte4) << "-"
         << (int)(mh->daddr.byte5) << "-"
         << (int)(mh->daddr.byte6) << ",";

    sout << std::dec
         << (int)(ih->daddr.byte1) << "."
         << (int)(ih->daddr.byte2) << "."
         << (int)(ih->daddr.byte3) << "."
         << (int)(ih->daddr.byte4) << ",";

    sout << std::hex
         << (int)(mh->saddr.byte1) << "-"
         << (int)(mh->saddr.byte2) << "-"
         << (int)(mh->saddr.byte3) << "-"
         << (int)(mh->saddr.byte4) << "-"
         << (int)(mh->saddr.byte5) << "-"
         << (int)(mh->saddr.byte6) << ",";

    sout << std::dec
         << (int)(ih->saddr.byte1) << "."
         << (int)(ih->saddr.byte2) << "."
         << (int)(ih->saddr.byte3) << "."
         << (int)(ih->saddr.byte4);
    m_ip_message = sout.str();
    return m_ip_message;
}

//将信息写入csv文件
void writeCSV(const struct pcap_pkthdr *header, const std::string m_ip_message)
{
    time_t local_tv_sec;

    /* convert the timestamp to readable format */
    struct tm *ltime;
    char timestr[100];
    local_tv_sec = header->ts.tv_sec;
    ltime = localtime(&local_tv_sec);
    strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", ltime);

    /* print ip addresse*/
    char info[60];

    sprintf(info, "%s,%s,%s,%s,%s\n",
            timestr, m_ip_message.c_str(), ftp[m_ip_message][0].c_str(), ftp[m_ip_message][1].c_str(), ftp[m_ip_message][2].c_str());

    out << info;
}